<?php
/**
 * Created by WXCH
 * Date: 2020/1/8 0008 18:32
 */

namespace app\api\service;

use app\api\model\Order as OrderModel;
use app\api\model\Product as ProductModel;
use app\lib\exception\OrderException;
use app\lib\exception\TokenException;
use think\Exception;
use think\Loader;
use think\Log;

//Loader::import('WxPay.WxPay', EXTEND_PATH, '.Data.php');
Loader::import('WxPay.WxPay', EXTEND_PATH, '.Api.php');

class Pay{

    const WXQUERY = 'https://api.mch.weixin.qq.com/pay/orderquery';//查询订单
    protected $uid;

    private $productID;
    protected $product;
    private $orderNo;
    private $orderTime;
    private $ip;

    public function pay($productID){
        if (!$productID) {
            throw new Exception('商品ID不允许为NULL');
        }
        $this->productID = $productID;
        $this->product = $this->getProductById($productID);

        return $this->makeWxPreOrder();
    }

    //查询订单状态返回给头条
    public function checkOrderStatus($tradeno){
        $rand = md5(time() . mt_rand(0, 1000));
        $params = [
            //'appid'            => self::WXAPPID,
            'appid'       => config("dou.WXAPPID"),
            //'mch_id'           => self::WXMCHID,
            'mch_id'       => config("dou.WXMCHID"),
            //'transaction_id'   => '',//微信订单号
            'out_trade_no'     => $tradeno,//商户订单号
            'nonce_str'        => $rand . '',
        ];
        $params['sign'] = $this->getWxSign($params);

        $xmldata = $this->ToXml($params);
        //$this->logs('log.txt', $xmldata);
        $resdata = $this->curlPost($xmldata,self::WXQUERY);
        $arr = $this->XmlToArr($resdata);

        if(($arr['return_code'] == 'SUCCESS') && ($arr['result_code'] == 'SUCCESS')){
//0：支付成功 1：支付超时 2：支付失败 3：支付关闭 4：支付取消
            if($arr['trade_state'] == 'SUCCESS'){
                //支付成功
                return [
                    'status' => 0
                ];
            }else if($arr['trade_state'] == 'NOTPAY'){
                return [
                    'status' => 4//订单未支付
                ];
            }else if($arr['trade_state'] == 'CLOSED'){
                return [
                    'status' => 3//订单已关闭
                ];
            }else{
                return [
                    'status' => 2//订单支付失败
                ];
            }
        }else{
            return [
                'status' => 2//支付失败
            ];
        }

    }

    private function makeWxPreOrder(){
        //创建订单
        $orderSnap = $this->snapOrder();

        $uid = Token::getCurrentUid();
        $mOrderModel = new OrderModel();
        $time1 = time();
        $orderNo = $this->makeOrderNo();
        $mOrderModel->order_no = $orderNo;
        $mOrderModel->user_id = $uid;
        //$mOrderModel->price = self::TOTAL_FEE;
        $mOrderModel->price = $this->product['price'];//分
        $mOrderModel->create_time = $time1;
        $mOrderModel->product_id = $this->productID;
        $mOrderModel->snap_thumb = $orderSnap['snapThumb'];
        $mOrderModel->snap_name = $orderSnap['snapName'];
        $mOrderModel->save();
        $this->ip = getRealIp();
        $this->orderNo = $orderNo;
        $this->orderTime = $time1;
        $openid = Token::getCurrentTokenVar('openid');
        if (!$openid) {
            throw new TokenException();
        }
        $wxresult = $this->wxOrderInfo();

        $zjtdresult = $this->zjtdOrderInfo($wxresult['mweb_url'], $openid);
        return $zjtdresult;
    }

//调用统一下单api
    public function wxOrderInfo(){
        $rand = md5(time() . mt_rand(0, 1000));
        $params = [
            //'appid'            => self::WXAPPID,
            'appid'       => config("dou.WXAPPID"),
            //'mch_id'           => self::WXMCHID,
            'mch_id'       => config("dou.WXMCHID"),
            'nonce_str'        => $rand . '',
            'body'             => '支付',
            'out_trade_no'     => $this->orderNo,
            //'total_fee'        => self::TOTAL_FEE,
            'total_fee'        => (int)($this->product['price']*100),
            'spbill_create_ip' => $this->ip,
            //'notify_url'       => self::NOTIFY_URL,
            'notify_url'       => config("dou.NOTIFY_URL"),
            'trade_type'       => 'MWEB',
            'scene_info'       => "{'h5_info':{'type':'wap','wap_name':'支付','wap_url':'http://x.xiaocenxu.com'}}"
        ];

        $params['sign'] = $this->getWxSign($params);
        $xmldata = $this->ToXml($params);
        //$this->logs('log.txt', $xmldata);
        $resdata = $this->curlPost($xmldata,'https://api.mch.weixin.qq.com/pay/unifiedorder');
        $arr = $this->XmlToArr($resdata);
        return $arr;
    }
    //字节跳动
    public function zjtdOrderInfo($mweb_url,$openid){

        $params = [
            //'merchant_id'          =>  self::TT_MERCHANT_ID,//头条支付分配给商户的商户号
            'merchant_id'          =>  config('dou.TT_MERCHANT_ID'),
            //'app_id'          =>  self::TT_APP_ID,//头条支付分配给商户 app_id，用于获取加签秘钥信息。
            'app_id'          =>  config('dou.TT_APP_ID'),//头条支付分配给商户 app_id，用于获取加签秘钥信息。
            'sign_type'       =>  'MD5',//固定值：MD5。商户生成签名的算法类型
            'timestamp'      => $this->orderTime.'',//发送请求的时间戳，精确到秒
            'version'   => '2.0',//固定值：2.0
            'trade_type'   => 'H5',//固定值：H5
            'product_code'   => 'pay',//固定值：pay
            'payment_type'   => 'direct',//固定值：direct
            'out_order_no'    =>  $this->orderNo,//商户订单号
            'uid'   => $openid,//用户在商户侧唯一标志，长度：32 位
            //'total_amount'   => self::TOTAL_FEE,//金额，整型，单位：分（不能有小数）
            'total_amount' => (int)($this->product['price']*100),
            'currency'   => 'CNY',//固定值: CNY。币种
            'subject'   => '微橱窗业务活动测试',//商户订单名称；如果是测试接入，请务必填写：xxx 业务xx 活动测试，例如，懂车帝xx 活动测试
            'body'   => '微橱窗活动',
            'trade_time'   => $this->orderTime.'',//下单时间戳，精确到秒
            'valid_time'   => '300',//订单有效时间（单位 秒）
            //'notify_url'   => self::NOTIFY_URL,//填任意非空 URL 即可（该字段含义为：银行卡支付的回调地址，未开通银行卡支付的开发者，该字段值填非空 URL 地址）
            'notify_url'       => config("dou.NOTIFY_URL"),
            //'alipay_url' => '',//调用支付宝 App 支付所需的支付请求参数（形如 'app_id=xxx&biz_content=xxx...'），详见支付宝 App 支付请求参数说明。
            'wx_url'        =>  $mweb_url,//调用微信 H5 支付统一下单接口 返回的 mweb_url 字段值（请注意不要进行 urlencode）。
            'wx_type'      => 'MWEB',//wx_url 非空时传 'MWEB'。wx_url 为空时，该字段不传
        ];

        $params['sign']  = $this->getTtSign($params);
        $params['risk_info'] = "{ip:'".$this->ip."'}";//支付风控参数。序列化后的 JSON 结构字符串，JSON 结构如下：{ip: '用户外网IP'}
        return $params;
    }

//头条签名
    public function getTtSign($params){
        //签名步骤一：按字典序排序参数
        ksort($params);
        $string = $this->ToUrlParams($params);
        //签名步骤二：在string后加入KEY
        //$string = $string . self::TT_SECRET;
        $string = $string . config('dou.TT_SECRET');
        //签名步骤三：MD5加密
        $result = MD5($string);
        return $result;
    }
//微信签名
    public function getWxSign($params){
        //签名步骤一：按字典序排序参数
        ksort($params);
        $string = $this->ToUrlParams($params);
        //签名步骤二：在string后加入KEY
        //$string = $string . "&key=".self::WXSECRET;
        $string = $string . "&key=".config('dou.WXSECRET');
        //签名步骤三：MD5加密
        $string = md5($string);
        //签名步骤四：所有字符转为大写
        $result = strtoupper($string);
        return $result;
    }
    public static function curlPost($postData = '', $url = ''){
        if (is_array($postData)) {
            $postData = http_build_query($postData);
        }
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数

        curl_setopt($ch, CURLOPT_HTTPHEADER, array("Expect:")); //头部要送出'Expect: '
        //curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4 );
        if(defined('CURLOPT_IPRESOLVE') && defined('CURL_IPRESOLVE_V4')) {
            curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
        }

        //https请求 不验证证书和host
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        $data = curl_exec($ch);
        curl_close($ch);
        return $data;
    }
    public function ToXml($values){
        $xml = "<xml>";
        foreach ($values as $key=>$val) {
            if (is_numeric($val)){
                $xml.="<".$key.">".$val."</".$key.">";
            }else{
                $xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
            }
        }
        $xml.="</xml>";
        return $xml;
    }
    /**
     * 格式化参数格式化成url参数
     */
    public function ToUrlParams($values){
        $buff = "";
        foreach ($values as $k => $v) {
            if($k != "sign" && $v != "" && !is_array($v)){
                $buff .= $k . "=" . $v . "&";
            }
        }

        $buff = trim($buff, "&");
        return $buff;
    }

    public function XmlToArr($xml){
        if($xml == '') return '';
        libxml_disable_entity_loader(true);
        $arr = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $arr;
    }

    private static function makeOrderNo(){
        $yCode = array('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J');
        $orderSn =
            $yCode[intval(date('Y')) - 2017] . strtoupper(dechex(date('m'))) . date(
                'd') . substr(time(), -5) . substr(microtime(), 2, 5) . sprintf(
                '%02d', rand(0, 99));
        return $orderSn;
    }

    // 生成订单快照
    private function snapOrder(){
        // status可以单独定义一个类
        $snap = [
            'price' => $this->product['price'],
            'snapName' => $this->product['name'],
            'snapThumb' => $this->product['thumb'],
            'fakenum' => $this->product['fakenum']
        ];
        return $snap;
    }

    // 根据订单查找真实商品
    private function getProductById($product_id){
        return ProductModel::get($product_id);
    }
}